package gui;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.Sprite;
import logic.LogicHelper;
import logic.Point;

/**
 * abstract template class for all menus
 * @author Rekin
 *
 */
public abstract class MenuCanvas extends Canvas{

	protected Sprite selectionElement1;
	protected Sprite selectionElement2;
	private Sprite[] flyingCards;
	Point extraItem1TopLeft;
	Point extraItem2TopLeft;
	private CanvasButton[] menuItems;
	private String[] menuItemsNames;
	private int elemHeight;
	private int selectedItemIndex;
	private int elementsCount;
	private Timer iTimer;
	private int moveImagesDelay;
	private int[] movementDirections;  //it represents direction of movement for each flying card, it's x(as i) & y(as i+1)
	
	/**
	 * method for creating sprites for button selection
	 */
	abstract protected void initSprites();
	
	/**
	 * constructor
	 * @param itemsNames table of String labels for buttons in menu
	 */
	public MenuCanvas(String[] itemsNames){
		menuItemsNames=itemsNames;
		elementsCount=menuItemsNames.length;
		elemHeight=countElementHeight();
		selectedItemIndex=0;
		menuItems=new CanvasButton[elementsCount];
		flyingCards=new Sprite[2];
		movementDirections=new int[2];
		setDirections();
		moveImagesDelay=0;
		initFlyingCards();
		initMenuElements();
		initTimer(20);
	}
	
	/**
	 * inits menu items
	 */
	private void initMenuElements(){
		int heightE=countElementHeight();
		int widthE=getWidth()*3/5;
		int elementsSpacingVert=elemHeight/3;
		int topOffset=topOffset();
		Point p;
    	for(int i=0; i<elementsCount;++i){
    		p=new Point((getWidth()-widthE)/2, topOffset+i*(elemHeight+elementsSpacingVert));
    		menuItems[i]=new CanvasButton(widthE, heightE, p, menuItemsNames[i]);
    	}
    	menuItems[selectedItemIndex].setSelected(true);
    	initSprites();
    	countNewSelectedPointsForSprites();
    	
    }
	
	/**
	 * counts single element height
	 * @return computed height
	 */
	private int countElementHeight(){
		return getHeight()/10; //one element is 10% total height
	}
	
	/**
	 * counts top offset for buttons
	 * @return top offset
	 */
	private int topOffset(){
		return ( getHeight() - (elementsCount-1)*elemHeight/2 - elementsCount*elemHeight ) / 2;
	}
	
	/**
	 * sets direction for left and right flying card
	 */
	private void setDirections(){
		Random rand = new Random();
		int val;
		while ((val = rand.nextInt() % 4) == 0) {
		}
		try {
			Thread.sleep(25);
		} catch (InterruptedException e) {
		}
		movementDirections[0] = val;
		
		val = rand.nextInt() % 5;
		while(val==0 || (val<0 && movementDirections[0]<0) || (val>0 && movementDirections[0]>0)){
			val = rand.nextInt() % 5;
		}
		movementDirections[1] = val;
	}
	
	/**
	 * painting elements
	 */
	protected void paint(Graphics g) {
		g.setColor(0,0,0);
		g.fillRect(0, 0, getWidth(), getHeight());	 //black background
		//paint moving cards in background
		for(int i=0;i<flyingCards.length;++i){
			flyingCards[i].paint(g);
		}
		
		for(int i=0;i<elementsCount;++i){
			menuItems[i].paintMe(g);
			if(menuItems[i].isSelected()){
				selectionElement1.paint(g);
				selectionElement2.paint(g);
			}
		}
	}
	
	/**
	 * handles selecting buttons
	 * @param oldElem old selected item
	 * @param newElem new selected item
	 */
	private void handleSelectionChange(int oldElem, int newElem){
		menuItems[oldElem].setSelected(false);
		menuItems[newElem].setSelected(true);
		selectedItemIndex=newElem;
		countNewSelectedPointsForSprites();
	}
	
	/**
	 * handles menu item press
	 * @param buttonIndex index of pressed button
	 */
	protected void menuItemPressed(int buttonIndex){
		if(buttonIndex<0){
			return;
		}
		handleSelectionChange(selectedItemIndex, buttonIndex);
    }
	
	/**
	 * button hit handling
	 * @param x hit x position
	 * @param y hit y position
	 * @return index of pressed button or -1
	 */
	private int buttonHit(int x, int y){
		for(int i=0;i<elementsCount;++i){
			int ex1=menuItems[i].getTopLeft().getX();
			int ex2=ex1 + menuItems[i].getMyWidth();
			int ey1=menuItems[i].getTopLeft().getY();
			int ey2=ey1 + menuItems[i].getMyHeight();
			
			if(x>=ex1 && x<=ex2 && y>=ey1 && y<=ey2){
				return i;
			}
		}
		return -1;
	}
	
	/**
	 * pressing pointer
	 */
	protected void pointerPressed(int x, int y){
		menuItemPressed(buttonHit(x,y));
	}
	
	/**
	 * pressing key
	 */
    protected void keyPressed(int keyCode){
    	int prevItemIndex=selectedItemIndex;
    	
    	switch(keyCode){
    	case Canvas.KEY_NUM5:
    	case Canvas.FIRE:
    		menuItemPressed(selectedItemIndex);
    		break;
    	case Canvas.KEY_NUM2:
    	case Canvas.UP:
    		--selectedItemIndex;
    		if(selectedItemIndex<0){
    			selectedItemIndex=elementsCount-1;
    		}
    		handleSelectionChange(prevItemIndex, selectedItemIndex);
    		break;
    	case Canvas.KEY_NUM8:
    	case Canvas.DOWN:
    		++selectedItemIndex;
    		if(selectedItemIndex>=elementsCount){
    			selectedItemIndex=0;
    		}
    		handleSelectionChange(prevItemIndex, selectedItemIndex);
    		break;
    	default:
    		break;
    	}
    }
    
    /**
     * positions selection elements on screen
     */
    private void countNewSelectedPointsForSprites(){
    	extraItem1TopLeft = new Point( menuItems[selectedItemIndex].getTopLeft().getX()-5-selectionElement1.getWidth(),
			menuItems[selectedItemIndex].getTopLeft().getY()+ (menuItems[selectedItemIndex].getMyHeight()-selectionElement1.getHeight())/2 );
		extraItem2TopLeft = new Point( menuItems[selectedItemIndex].getTopLeft().getX()+menuItems[selectedItemIndex].getMyWidth()+5,
			menuItems[selectedItemIndex].getTopLeft().getY()+ (menuItems[selectedItemIndex].getMyHeight()-selectionElement2.getHeight())/2 );
    
		selectionElement1.setPosition(extraItem1TopLeft.getX(), extraItem1TopLeft.getY());
		selectionElement2.setPosition(extraItem2TopLeft.getX(), extraItem2TopLeft.getY());
    }
    
    /**
     * initiates left and right flying card
     */
    private void initFlyingCards(){
    	for(int i=0;i<flyingCards.length;++i){
    		flyingCards[i]=new Sprite(LogicHelper.createImageFromResources("/flyingCards/card"+(i+1)+".png", this));
    		if(i==0){
    			flyingCards[i].setPosition(5, randY());
    		}
    		else if(i==1){
    			flyingCards[i].setPosition(getWidth()-flyingCards[i].getWidth()-5, randY());
    		}
    		try{
    		Thread.sleep(25);
    		}
    		catch(InterruptedException e){}
    	}
    }
    
    /**
     * handles animation of flying cards
     */
    private void animateFlyingCards(){
    	handleOutOfBounds();
    	for(int i=0;i<flyingCards.length;++i){
    		flyingCards[i].setPosition(flyingCards[i].getX(), flyingCards[i].getY()+movementDirections[i]);
    	}
    }
    
    /**
     * handles bounds hits for cards
     */
    private void handleOutOfBounds(){
    	for(int i=0;i<flyingCards.length;++i){
    		if(flyingCards[i].getY()<1) 
    			movementDirections[i]=Math.abs(movementDirections[i]);
    		else if(flyingCards[i].getY()+flyingCards[i].getHeight()>getHeight()-1)
    			movementDirections[i]=-Math.abs(movementDirections[i]);
    	}
    }
    
    /**
     * randomises Y position - used for cards
     * @return
     */
    private int randY(){
    	Random rand=new Random();
    	int val=getHeight()/2+rand.nextInt()%5;
    	return val;
    }
    
    /**
     * starts timer moving the cards
     * @param withIntervalMilis refresh rate
     */
    private void initTimer(int withIntervalMilis){
    	if(withIntervalMilis<=0){
    		return;
    	}
    	iTimer = new Timer();
		iTimer.schedule(new TimerTask(){
			public void run(){
				repaint();
				animateFlyingCards();
				if(moveImagesDelay==2){  //selection sprites will move twice slower than flying cards
					selectionElement1.nextFrame(); 
					selectionElement2.nextFrame();
					moveImagesDelay=0;
				}
				else{
					++moveImagesDelay;
				}
			} 
		} , 0, withIntervalMilis);
    }

    /**
     * returns menu item
     * @param index index of item to be returned
     * @return desired CanvasButton
     */
	public CanvasButton getMenuItem(int index) {
		return menuItems[index];
	}
}
